
package com.jf.cloud.user.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.jf.cloud.api.coupon.bo.CouponGiveBO;
import com.jf.cloud.api.coupon.bo.CouponGiveBizType;
import com.jf.cloud.api.coupon.bo.CouponIdAndCountBO;
import com.jf.cloud.api.coupon.feign.CouponFeignClient;
import com.jf.cloud.api.coupon.vo.CouponDataVO;
import com.jf.cloud.api.user.bo.RechargeNotifyBO;
import com.jf.cloud.common.cache.constant.CacheNames;
import com.jf.cloud.common.constant.Constant;
import com.jf.cloud.common.database.dto.PageDTO;
import com.jf.cloud.common.database.util.PageUtil;
import com.jf.cloud.common.database.vo.PageVO;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.response.ResponseEnum;
import com.jf.cloud.common.response.ServerResponseEntity;
import com.jf.cloud.common.rocketmq.config.RocketMqConstant;
import com.jf.cloud.common.util.PriceUtil;
import com.jf.cloud.common.util.PrincipalUtil;
import com.jf.cloud.user.constant.*;
import com.jf.cloud.user.dto.RechargeCouponDTO;
import com.jf.cloud.user.dto.UserAdminDTO;
import com.jf.cloud.user.dto.UserRechargeDTO;
import com.jf.cloud.user.mapper.UserBalanceLogMapper;
import com.jf.cloud.user.mapper.UserGrowthLogMapper;
import com.jf.cloud.user.mapper.UserRechargeMapper;
import com.jf.cloud.user.mapper.UserScoreLogMapper;
import com.jf.cloud.user.model.*;
import com.jf.cloud.user.service.UserBalanceLogService;
import com.jf.cloud.user.service.UserExtensionService;
import com.jf.cloud.user.service.UserRechargeCouponService;
import com.jf.cloud.user.service.UserRechargeService;
import com.jf.cloud.user.vo.UserRechargeVO;
import ma.glasnost.orika.MapperFacade;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 余额充值级别表
 *
 * @author zz
 * @date 2021-04-27 15:51:36
 */
@Service
public class UserRechargeServiceImpl implements UserRechargeService {

    @Autowired
    private UserRechargeMapper userRechargeMapper;
    @Autowired
    private UserExtensionService userExtensionService;
    @Autowired
    private UserBalanceLogService userBalanceLogService;
    @Autowired
    private UserBalanceLogMapper userBalanceLogMapper;
    @Autowired
    private CouponFeignClient couponFeignClient;
    @Autowired
    private MapperFacade mapperFacade;
    @Autowired
    private UserRechargeCouponService userRechargeCouponService;
    @Autowired
    private RocketMQTemplate couponGiveTemplate;
    @Autowired
    private UserGrowthLogMapper userGrowthLogMapper;
    @Autowired
    private UserScoreLogMapper userScoreLogMapper;

    @Override
    public PageVO<UserRecharge> page(PageDTO pageDTO) {
        return PageUtil.doPage(pageDTO, () -> userRechargeMapper.list());
    }

    @Override
    @Cacheable(cacheNames = CacheNames.USER_RECHARGE_INFO, key = "#rechargeId")
    public UserRechargeVO getRechargeInfo(Long rechargeId) {
        UserRechargeVO userRechargeVO = userRechargeMapper.getRechargeInfo(rechargeId);
        return userRechargeVO;
    }

    @Override
    @CacheEvict(cacheNames = CacheNames.USER_RECHARGE_LIST, allEntries = true)
    public void save(UserRecharge userRecharge) {
        userRechargeMapper.save(userRecharge);
    }

    @Override
    public void update(UserRecharge userRecharge) {
        userRechargeMapper.update(userRecharge);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteById(Long rechargeId) {
        userRechargeMapper.deleteById(rechargeId);
        userRechargeCouponService.deleteById(rechargeId);
    }

    @Override
    @Cacheable(cacheNames = CacheNames.USER_RECHARGE_LIST)
    public List<UserRechargeVO> list() {
        List<UserRechargeVO> list = userRechargeMapper.list();
        List<Long> couponIds = new ArrayList<>();
        for (UserRechargeVO userRechargeVO : list) {
            List<CouponDataVO> couponList = userRechargeVO.getCouponList();
            if (CollUtil.isNotEmpty(couponList)) {
                couponIds.addAll(couponList.stream().map(CouponDataVO::getCouponId).collect(Collectors.toList()));
            }
        }
        if (CollUtil.isNotEmpty(couponIds)) {
            ServerResponseEntity<List<CouponDataVO>> couponEntity = couponFeignClient.getCouponListByCouponIds(couponIds);
            if (couponEntity.isFail()) {
                throw new LuckException(couponEntity.getMsg());
            }
            List<CouponDataVO> couponDataVOList = couponEntity.getData();
            Map<Long, String> couponMap = couponDataVOList.stream().collect(Collectors.toMap(CouponDataVO::getCouponId, CouponDataVO::getCouponName));
            for (UserRechargeVO userRechargeVO : list) {
                userRechargeVO.setCouponTotalNum(userRechargeVO.getCouponList().size());
                for (CouponDataVO couponDataVO : userRechargeVO.getCouponList()) {
                    Long couponId = couponDataVO.getCouponId();
                    if (couponMap.containsKey(couponId)) {
                        couponDataVO.setCouponName(couponMap.get(couponId));
                    }
                }
            }
        }
        return list;
    }

    @Override
    @Caching(evict = {
            @CacheEvict(cacheNames = CacheNames.USER_RECHARGE_LIST, allEntries = true),
            @CacheEvict(cacheNames = CacheNames.USER_RECHARGE_INFO, key = "#rechargeId")})
    public void removeCacheByRechargeId(Long rechargeId) {
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean batchUpdateUserBalance(UserAdminDTO userAdminDTO) {
        BigDecimal balance= userAdminDTO.getBalanceValue();
        if (!PrincipalUtil.isMaximumOfTwoDecimal(balance.toString())) {
            throw new LuckException("最多是保留两位小数的数值"+userAdminDTO.toString());
        }
        // 将元转为分
        long balanceValue = PriceUtil.toLongCent(balance);
        List<Long> userIds = userAdminDTO.getUserIds();
        if (Objects.equals(0L,balanceValue) || CollUtil.isEmpty(userIds)){
            return false;
        }
        DateTime now = DateUtil.date();
        // 批量给用户充值
        List<UserExtension> extensions = userExtensionService.getByUserIdsAndLevelType(userIds);
        List<Long> userIdsUpdate =extensions.stream().map(UserExtension::getUserId).collect(Collectors.toList());
        List<UserBalanceLog> userRechargeLogs = new ArrayList<>();
        for (UserExtension extension : extensions) {
            if (Objects.isNull(extension)){
                continue;
            }
            // 用户改变的余额数值
            long changeBalance = balanceValue;
            long totalBalance = Objects.nonNull(extension.getActualBalance())? extension.getActualBalance(): 0L;
            int compare = Long.compare(PriceUtil.MAX_CENT - extension.getBalance(), balanceValue);
            // 用户余额的balance的最大值为Long.MAX_VALUE，当用户余额 + 新增余额达到这个值的时候，用户将不能在继续充值了
            if (compare < 0 && userIds.size() == 1) {
                throw new LuckException("当前用户余额已达到最大限额，剩余可充值余额为：" + PriceUtil.toDecimalPrice(PriceUtil.MAX_CENT - extension.getBalance()));
            } else if (compare < 0 && userIds.size() > 1)  {
                continue;
            }
            long userBalance = totalBalance + balanceValue;
            if(userBalance < 0){
                //修改后的金额 < 0 ，那么减少的金额就是用户原本的余额
                changeBalance = totalBalance;
            }
            else if (userBalance > Constant.MAX_USER_BALANCE) {
                changeBalance = Constant.MAX_USER_BALANCE - totalBalance;
            }
            // 添加日志
            UserBalanceLog userRechargeLog = new UserBalanceLog();
            userRechargeLog.setUserId(extension.getUserId());
            userRechargeLog.setChangeBalance(changeBalance);
            userRechargeLog.setIoType(balanceValue>0? RechargeIoTypeEnum.INCOME.value():RechargeIoTypeEnum.EXPENDITURE.value());
            userRechargeLog.setType(RechargeTypeEnum.SYSTEM.value());
            userRechargeLog.setPayId(userAdminDTO.getShopId());
            userRechargeLogs.add(userRechargeLog);
        }
        if (CollUtil.isEmpty(userIdsUpdate)) {
            return false;
        }
        userExtensionService.updateBatchUserBalanceByUserIds(userIdsUpdate,balanceValue,now);
        userBalanceLogService.saveBatch(userRechargeLogs);
        return true;
    }

    @Override
    public UserRechargeVO getRechargeByRechargeId(Long rechargeId, Integer putOnStatus) {
        if (Objects.isNull(userRechargeMapper.getByRechargeId(rechargeId))){
            throw new LuckException("数据状态错误，请刷新重试");
        }
        UserRechargeVO userRechargeVO = getRechargeInfo(rechargeId);
        List<CouponDataVO> couponDataList = userRechargeVO.getCouponList();
        if (CollUtil.isEmpty(couponDataList)) {
            return userRechargeVO;
        }
        List<Long> couponIds = couponDataList.stream().map(CouponDataVO::getCouponId).collect(Collectors.toList());
        // 查询优惠券信息
        ServerResponseEntity<List<CouponDataVO>> couponResponse = couponFeignClient.getCouponListByCouponIdsAndPutOnStatus(couponIds, putOnStatus);
        if (!Objects.equals(couponResponse.getCode(), ResponseEnum.OK.value())) {
            throw new LuckException(couponResponse.getMsg());
        }
        List<CouponDataVO> couponDataVOList = couponResponse.getData();
        if (CollUtil.isEmpty(couponDataVOList)) {
            return userRechargeVO;
        }
        Map<Long, CouponDataVO> couponDataMap = couponDataList.stream().collect(Collectors.toMap(CouponDataVO::getCouponId, (k) -> k));
        for (CouponDataVO couponDataVO : couponDataVOList) {
            CouponDataVO dataVO = couponDataMap.get(couponDataVO.getCouponId());
            couponDataVO.setCouponNum(dataVO.getCouponNum());
        }
        userRechargeVO.setCouponList(couponDataVOList);
        return userRechargeVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = CacheNames.USER_RECHARGE_LIST, allEntries = true)
    public boolean saveRecharge(UserRechargeDTO userRechargeDTO) {
        UserRecharge userRecharge = mapperFacade.map(userRechargeDTO, UserRecharge.class);
        userRechargeMapper.save(userRecharge);
        if (CollUtil.isEmpty(userRechargeDTO.getCouponList())){
            return false;
        }
        userRechargeCouponService.insertBatch(userRecharge.getRechargeId(),userRechargeDTO.getCouponList());
        return true;
    }

    @Override
    public void updateByRechargeId(UserRechargeDTO userRechargeDTO) {
        if (Objects.isNull(userRechargeMapper.getByRechargeId(userRechargeDTO.getRechargeId()))){
            throw new LuckException("数据状态错误，请刷新重试");
        }
        UserRecharge userRecharge = mapperFacade.map(userRechargeDTO, UserRecharge.class);
        userRechargeMapper.update(userRecharge);
        UserRechargeVO userRechargeDb = getRechargeByRechargeId(userRecharge.getRechargeId(), null);
        Map<Long, CouponDataVO> mapByCouponId = userRechargeDb.getCouponList().stream().collect(Collectors.toMap(CouponDataVO::getCouponId, c -> c));
        List<RechargeCouponDTO> addCoupon = Lists.newArrayList();
        if (CollUtil.isEmpty(userRechargeDTO.getCouponList()) && CollUtil.isNotEmpty(userRechargeDb.getCouponList())){
            // 之前赠送优惠券，更新为不赠送优惠券
            userRechargeCouponService.deleteById(userRecharge.getRechargeId());
        }else if (CollUtil.isEmpty(userRechargeDb.getCouponList()) && CollUtil.isNotEmpty(userRechargeDTO.getCouponList())){
            // 之前不赠送优惠券，更新为赠送优惠券
            userRechargeCouponService.insertBatch(userRechargeDTO.getRechargeId(),userRechargeDTO.getCouponList());
        }else {
            //筛选需要删除的优惠券.
            Set<Long> couponIdList = mapByCouponId.keySet();
            List<RechargeCouponDTO> updateList = Lists.newArrayList();

            for(RechargeCouponDTO couponDto: userRechargeDTO.getCouponList()){
                if (!couponIdList.contains(couponDto.getCouponId())){
                    addCoupon.add(couponDto);
                    continue;
                }
                CouponDataVO couponDb = mapByCouponId.get(couponDto.getCouponId());
                if (Objects.equals(couponDto.getCouponNum(),couponDb.getCouponNum())) {
                    couponIdList.remove(couponDto.getCouponId());
                    continue;
                }
                updateList.add(couponDto);
                couponIdList.remove(couponDto.getCouponId());
            }
            if(CollUtil.isNotEmpty(couponIdList)){
                userRechargeCouponService.removeByRechargeIdAndCouponId(userRechargeDTO.getRechargeId(), couponIdList);
            }
            if(CollUtil.isNotEmpty(addCoupon)){
                userRechargeCouponService.insertBatch(userRechargeDTO.getRechargeId(),addCoupon);
            }
            if (CollUtil.isNotEmpty(updateList)){
                userRechargeCouponService.updateBatchByCoupons(userRechargeDTO.getRechargeId(),updateList);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rechargeSuccess(RechargeNotifyBO message) {
        Long balanceLogId = message.getBalanceLogId();
        UserBalanceLog userBalanceLogDb = userBalanceLogMapper.getByBalanceLogId(balanceLogId);
        if (Objects.equals(userBalanceLogDb.getIsPayed(), 1)) {
            return;
        }
        Long rechargeId = userBalanceLogDb.getRechargeId();
        UserRechargeVO userRechargeVO = null;
        if (Objects.nonNull(rechargeId)) {
            userRechargeVO = getRechargeInfo(rechargeId);
        }
        List<CouponDataVO> couponGiveList = null;
        if (userRechargeVO != null) {
            // 增加成长值、积分、余额
            userExtensionService.updateByUserRecharge(userRechargeVO,userBalanceLogDb);
            couponGiveList = userRechargeVO.getCouponList();
            addPreLog(userBalanceLogDb, userRechargeVO);
        } else {
            // 更新用户余额
            userExtensionService.addByUserBalanceLog(userBalanceLogDb);
        }
        // 更新支付状态
        int updateStatus = userBalanceLogMapper.updateToSuccess(balanceLogId, message.getPayId(), message.getPayType());
        if (updateStatus < 1) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }

        // 没有赠送优惠券，则返回
        if (CollectionUtil.isEmpty(couponGiveList)) {
            return;
        }
        // 处理赠送优惠券
        CouponGiveBO couponGiveBO = new CouponGiveBO();

        List<CouponIdAndCountBO> couponInfos = new ArrayList<>();


        for (CouponDataVO couponDataVO : couponGiveList) {
            CouponIdAndCountBO couponIdAndCountBO = new CouponIdAndCountBO();
            couponIdAndCountBO.setCouponId(couponDataVO.getCouponId());
            couponIdAndCountBO.setCount(couponDataVO.getCouponNum());
            couponInfos.add(couponIdAndCountBO);
        }
        couponGiveBO.setUserId(userBalanceLogDb.getUserId());
        couponGiveBO.setBizId(userBalanceLogDb.getBalanceLogId());
        couponGiveBO.setBizType(CouponGiveBizType.RECHARGE.value());
        couponGiveBO.setCouponInfos(couponInfos);
        // 发送消息，赠送优惠券
        SendStatus sendStatus = couponGiveTemplate.syncSend(RocketMqConstant.COUPON_GIVE_TOPIC, new GenericMessage<>(couponGiveBO)).getSendStatus();
        if (!Objects.equals(sendStatus,SendStatus.SEND_OK)) {
            // 消息发不出去就抛异常，因为订单回调会有多次，几乎不可能每次都无法发送出去，发的出去无所谓因为接口是幂等的
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    /**
     * 添加余额充值赠送的余额、积分、成长值记录
     * @param userBalanceLogDb
     * @param userRechargeVO
     */
    private void addPreLog(UserBalanceLog userBalanceLogDb, UserRechargeVO userRechargeVO) {
        // 添加余额赠送记录
        if (userRechargeVO.getPresAmount() > 0) {
            UserBalanceLog userBalanceLog = new UserBalanceLog();
            userBalanceLog.setUserId(userBalanceLogDb.getUserId());
            userBalanceLog.setChangeBalance(userRechargeVO.getPresAmount());
            userBalanceLog.setIoType(RechargeIoTypeEnum.INCOME.value());
            userBalanceLog.setType(RechargeTypeEnum.PRESENT.value());
            userBalanceLogMapper.save(userBalanceLog);
        }
        // 添加积分赠送记录
        if (userRechargeVO.getPresScore() > 0) {
            UserScoreLog userScoreLog = new UserScoreLog();
            userScoreLog.setUserId(userBalanceLogDb.getUserId());
            userScoreLog.setSource(ScoreLogTypeEnum.BALANCE.value());
            userScoreLog.setScore(userRechargeVO.getPresScore());
            userScoreLog.setBizId(userBalanceLogDb.getBalanceLogId());
            userScoreLog.setIoType(ScoreIoTypeEnum.INCOME.value());
            userScoreLogMapper.save(userScoreLog);
        }
        // 添加成长值赠送记录
        if (userRechargeVO.getPresGrowth() > 0) {
            UserGrowthLog userGrowthLog = new UserGrowthLog();
            userGrowthLog.setUserId(userBalanceLogDb.getUserId());
            userGrowthLog.setSource(GrowthLogSourceEnum.BALANCE.value());
            userGrowthLog.setBizId(userBalanceLogDb.getBalanceLogId());
            userGrowthLog.setChangeGrowth(userRechargeVO.getPresGrowth());
            userGrowthLog.setRemarks("余额充值赠送的成长值");
            userGrowthLogMapper.save(userGrowthLog);
        }
    }

}
